package com.ydj.framework.redis;

import com.ydj.framework.exception.CommonException;
import com.ydj.framework.exception.DistributedLockException;
import com.ydj.framework.utils.DateUtils;
import com.ydj.framework.utils.ThreadUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.function.Supplier;

/**
 * 分布式锁对象<br>
 * 用户辅助加锁操作
 *
 * @author wangsc
 * @time 2018年11月5日下午7:34:02
 */

@Slf4j
@Component
public class RedisDb {
    // 分布式锁，超时的时间
    private static final long DISTRIBUTED_LOCK_DEFAULT_TIMEOUT = 10 * DateUtils.SECOND;
    // 分布式锁，尝试获取的次数
    private static final int DISTRIBUTED_LOCK_TRY_COUNT = 1;

    @Autowired
    private ExtRedisTemplate redisTemplate;

    RedisDistributedLockHandlerImpl distributedLockHandler;

    @PostConstruct
    private void init(){
        distributedLockHandler=new RedisDistributedLockHandlerImpl();
        distributedLockHandler.setRedisTemplate(redisTemplate);
    }

    public ExtRedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public Boolean checkLock(String name) {
        return redisTemplate.hasKey(name);
    }

    /**
     * 分布式锁, 非租塞乐观锁
     *
     * @param name 锁的名字
     * @param func 运行的函数
     * @param <T>  目标函数返回的类型
     * @return 目标函数的返回值
     * @throws DistributedLockException 当获取锁失败时候，会直接发生异常
     */
    public <T> T lock(String name, RedisLockSupplier<T> func) throws DistributedLockException, CommonException {
        return lock(name, DISTRIBUTED_LOCK_DEFAULT_TIMEOUT, func, null);
    }

    /**
     * 分布式锁, 非租塞乐观锁
     *
     * @param name    锁的名字
     * @param timeout 超时时间
     * @param func    运行的函数
     * @param <T>     目标函数返回的类型
     * @return 目标函数的返回值
     * @throws DistributedLockException 当获取锁失败时候，会直接发生异常
     */
    public <T> T lock(String name, long timeout, RedisLockSupplier<T> func, String distributedLockMsg) throws DistributedLockException, CommonException {
        DistributedLock lock = new DistributedLock(name, timeout);
        if (distributedLockHandler.tryLock(lock)) {
            try {
                return func.get();
            } catch (CommonException e) {
                throw e;
            } finally {
                //释放锁
                distributedLockHandler.releaseLock(lock);
            }
        } else {
            throw new DistributedLockException(distributedLockMsg);
        }
    }

    public Boolean distributedLock(DistributedLock lock){
        return distributedLockHandler.tryLock(lock);
    }

    //String key, String value, long lockExpire, int retryTimes, long sleepMillis
    public Boolean distributedLock(DistributedLock newLock,int  retryTimes,long sleepMillis){
        return distributedLockHandler.tryLock(newLock.getLockKey(), newLock.getUUID().toString(), newLock.getExpiryTime(),retryTimes,sleepMillis);
    }

    public void releaseLock(DistributedLock lock){
        //释放锁
        distributedLockHandler.releaseLock(lock);
    }

    @FunctionalInterface
    public interface RedisLockSupplier<T> {

        /**
         * Gets a result.
         *
         * @return a result
         */
        T get() throws CommonException;
    }

}